#ifndef _TENACITAS__ASYNC__TEST__FIRST__H_
#define _TENACITAS__ASYNC__TEST__FIRST__H_
/** @file first.h
    This file shows how to configure a tenacitas::async::handlers.
    There is only one type of event_1, one handler, and the handler creator is a
    separate class from the handler class 
*/

/** @example first.h */

#include <cstdint>
#include <string>
#include <tuple>
#include <ctime>

#include <boost/thread/thread.hpp>
#include <boost/thread/mutex.hpp>

#include <tenacitas_async/handlers.h>
#include <tenacitas_async/unhandled.h>

//#include <thread>
//#include <mutex>

#include "logger.h"

using namespace std;
using namespace tenacitas;



namespace tenacitas {
    namespace async {
        /** namepsace created for the first test of tenacitas::async */
        namespace test_1 {

            static const uint16_t g_num_tests = 25;

            /** The handler class that will handle the event_1 */

            class handler_a {
            public:                               


                /** Contructor */
                handler_a( logger_test::ptr_t p_logger )
                    : m_mutex( ),
                      m_logger( p_logger ) {}

                /** The operator that handles the event_1 */
                void operator( ) ( uint16_t p_i, std::string p_s ) {

                    boost::lock_guard<boost::mutex> l_lock(m_mutex);
          
                    log_begin( m_logger );

                    // taking it easy...
                    uint16_t l_off = g_num_tests - p_i;
                    log_info( m_logger ) << "sleeping for " << l_off
                                         << " seconds, before handling "
                                         << "p_i = " << p_i << ", "
                                         << "p_s = '" << p_s << "'";
              
                    boost::this_thread::sleep(
                        boost::posix_time::seconds( l_off ) ); 

                    log_info( m_logger ) << "waking up to handle "
                                         << "p_i = " << p_i << ", "
                                         << "p_s = '" << p_s << "'";
        
                    log_end( m_logger );

                }

            private:
                /** A handler is to be called in a multi-threaded envinronment */
                boost::mutex m_mutex;

                logger_test::ptr_t m_logger;
            };

            // The handler creator class
	
            class handler_factory_a {
            public:

                typedef handler_a handler_t;

                typedef std::shared_ptr<handler_t> handler_ptr_t;

                handler_factory_a( logger_test::ptr_t p_logger )
                    : m_first_creation_counter( 0 ),
                      m_additional_creation_counter( 0 ),
                      m_first_creation_max( 4 ),
                      m_additional_creation_max( 2 ),
                      m_logger( p_logger ) {}

                /**  Method that will create the first m_first_creation_max
                     handler_ptr_t objects */              
                handler_ptr_t first_creation( ) {
                    if ( m_first_creation_counter < m_first_creation_max ) {
                        ++m_first_creation_counter;
                        return ( handler_ptr_t( new handler_t ( m_logger ) ) );
                    }
                    return ( handler_ptr_t( ) );
                }

                // Method that will create additional m_additional_creation_max
                // handler::ptr_t objects                
                handler_ptr_t additional_creation( ) {
                    if ( m_additional_creation_counter < m_additional_creation_max ) {
                        ++m_additional_creation_counter;
                        return ( handler_ptr_t( new handler_t ( m_logger ) ) );
                    }
                    return ( handler_ptr_t( ) );
                }
            private:

                uint16_t m_first_creation_counter;
                uint16_t m_additional_creation_counter;

                const uint16_t m_first_creation_max;
                const uint16_t m_additional_creation_max;

                logger_test::ptr_t m_logger;
            };


           
            /** Runs the test

                @tparam t_logger is left as a template, so that the same logger
                can be used by many tests
            */
            void run( logger_test::ptr_t p_logger ) {


                typedef unhandled<logger_test, int, std::string> unhandled_events_t;

                /** Type for the handlers class */
                typedef handlers<handler_factory_a,
                                 unhandled_events_t,
                                 logger_test,
                                 int, std::string> handlers_t;

                typedef typename handlers_t::handler_factory_t
                    handler_factory_t;

                typedef typename handlers_t::handler_factory_ptr_t
                    handler_factory_ptr_t;

                typedef typename handlers_t::unhandled_events_ptr_t
                    unhandled_events_ptr_t;
                
                
                // lets go!!

                log_begin( p_logger );

                // remember that handler_factory will initialy create only 2
                // handlers, and an extra one each time handlers cant find a free
                // handler

                uint16_t l_num_events ( g_num_tests );

                // to format the string part of the event_1
                stringstream l_stream;

                // to count the events fired
                uint16_t l_counter ( 0 );

                // creating the handler creator
                handler_factory_ptr_t
                    l_handler_factory ( new handler_factory_a( p_logger ) );

                // creating the unhandled events list
                unhandled_events_ptr_t
                    l_unhandled_events( new unhandled_events_t( p_logger ) );
                

                handlers_t l_handlers( l_handler_factory,
                                       l_unhandled_events,
                                       p_logger );

                while ( true ) {

                    if ( l_counter == l_num_events ) {
                        break;
                    }
                    
                    // breathing...
                    boost::this_thread::sleep(boost::posix_time::millisec(10));

                    log_debug( p_logger ) << string( "event number " ) << l_counter;

                    // formating the string part of the event 
                    l_stream.str( "" );
                    l_stream << string( "counter " ) << l_counter;
                    
                    // sending the event_1 to be handled
                    l_handlers.handle( l_counter, l_stream.str( ) );

                    ++l_counter;
                }
            }
        }

    }
}

#endif
